feat(seo): AI summary enrichment backfill (slice 3) - #113
Merged
Conversation
Adds 5 provenance columns to the servers table (ai_summary, generated_at, model, prompt_version, source_hash) plus a sparse index for the nightly orchestrator's incremental selection query. No search_vector changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ai_summary column comment said to "prefer over description for structured-data SoftwareApplication.description" — this contradicts the accepted recovery plan's anti scaled-content-abuse guardrail (specs/stage-6-slices/00-recovery-plan.md, "AI summary is a labeled supplement, never the page body"). Corrected the comment to state the opposite: labeled supplement only, never substituted into JSON-LD description or used as page body copy. Slice 3 of the indexing-recovery plan.
Adds scripts/mcpfind/ai_summary_generate.py — the nightly orchestrator that backfills ai_summary for gated (isIndexable) servers, per specs/stage-6-slices/00-recovery-plan.md Slice 3. - Ports apps/web/lib/indexable.ts#isIndexable to Python 1:1 (same README_MIN_LENGTH/MIN_SIGNALS thresholds) so only servers already clearing the real-data quality bar get an AI summary. - Computes a source hash matching migration 006's documented ai_summary_source_hash inputs (name|description|readme_content[:8000]| category|package_name|package_type|registry_tags) and skips rows whose hash is unchanged — idempotent, no wasted regeneration. - Generates via the local Mac Mini Claude Code queue (127.0.0.1:7600), never a raw Anthropic API key, consistent with this org's convention. No prior client for that queue existed in this repo (grepped for "7600" and "claude-code-local-queue" — no hits outside the migration comments), so the request/response contract is documented as an assumption in the module docstring; only call_local_queue() needs updating if the real contract differs. - Reads SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY, matching the existing convention in packages/sync/src/index.ts and recategorize-other.ts (the anon key is read-only under RLS and cannot write ai_summary_*). - argparse CLI (--dry-run, --limit, --prompt-version, --page-size, --sleep-seconds, -v), structured logging, per-row error isolation so one bad row doesn't abort the whole nightly run (exit 2 signals partial failure, safe to re-run).
…ytecode Running the new ai_summary_generate.py script locally generated a .pyc that got swept into the previous commit's `git add scripts/mcpfind/`. Untrack it and add __pycache__/ and *.pyc to .gitignore so this doesn't recur — this repo has no prior Python tooling/gitignore entries to reuse.
… pages
Slice 3 of the indexing-recovery plan
(specs/stage-6-slices/00-recovery-plan.md) — renders ai_summary (migration
006) as a clearly labeled "AI Summary" callout block, positioned near the
top of the main content column, right before the README section.
- packages/shared/src/types.ts: add ai_summary / ai_summary_generated_at to
Server. Excluded from ServerListItem (same rationale as readme_content —
detail-page only, not selected by SERVER_LIST_COLUMNS).
- apps/web/lib/queries.ts: SERVER_DETAIL_COLUMNS now selects ai_summary and
ai_summary_generated_at alongside readme_content.
- apps/web/app/servers/[slug]/page.tsx: new section, rendered only when
server.ai_summary is non-null, visually distinct (bordered callout,
IconRobot, "AI Summary" heading) and explicitly labeled as a supplement
("supplements, not a substitute for the README below").
Guardrail verified, not changed: apps/web/lib/metadata.ts's
generateServerJsonLd already builds SoftwareApplication.description from
server.description (never ai_summary) — confirmed by reading the function
and grepping the whole apps/+packages/ tree for "ai_summary"; no other
consumer exists. This is the hard guardrail from the recovery plan's
"AI summary is a labeled supplement, never the page body" section.
Verified: pnpm --filter @mcpfind/shared build, pnpm --filter @mcpfind/web
type-check, and pnpm --filter @mcpfind/web test (206/206 passing) all clean
after this change.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Slice 3 (Enrichment backfill) of the indexing-recovery plan —
specs/stage-6-slices/00-recovery-plan.md. Merges the previously-orphanedmcpfind-ai-summarybranch (migration 006) forward onto currentmain, fixes a guardrail bug in that migration's column comment, and adds the nightly backfill script + labeled UI rendering.ai_summarycolumn comment said to "prefer over description for structured-data SoftwareApplication.description" — this directly contradicted the accepted recovery plan's guardrail ("AI summary is a labeled supplement, never the page body"). Corrected to say the opposite: never substitute into JSON-LD description or page body copy.scripts/mcpfind/ai_summary_generate.py(new): nightly orchestrator that portsapps/web/lib/indexable.ts#isIndexable1:1 to Python for gating, computes a source hash matching migration 006's documented hash inputs, skips unchanged rows, and generates via the local Mac Mini Claude Code queue (127.0.0.1:7600) — never a raw Anthropic API key. No prior client for that queue existed in this repo, so the request/response contract is documented as an explicit assumption in the module docstring (onlycall_local_queue()needs updating if the real contract differs).ai_summaryas a clearly labeled "AI Summary" callout block near the top of the main content column, only when non-null. Confirmed (unchanged) thatgenerateServerJsonLdinapps/web/lib/metadata.tsbuildsSoftwareApplication.descriptionfromserver.descriptiononly — neverai_summary.Files changed
supabase/migrations/006_ai_summary.sql— column comment guardrail fixscripts/mcpfind/ai_summary_generate.py— new nightly backfill scriptscripts/mcpfind/requirements.txt— new,requestsdependencypackages/shared/src/types.ts—Server.ai_summary/ai_summary_generated_at, excluded fromServerListItemapps/web/lib/queries.ts—SERVER_DETAIL_COLUMNSnow selects the new columnsapps/web/app/servers/[slug]/page.tsx— new "AI Summary" supplement block.gitignore—__pycache__/,*.pyc(new, this repo had no prior Python tooling)Test plan
pnpm --filter @mcpfind/shared build— cleanpnpm --filter @mcpfind/web type-check— cleanpnpm --filter @mcpfind/web test— 206/206 passingpython3 -m py_compile scripts/mcpfind/ai_summary_generate.py— cleanis_indexable()/compute_source_hash()(3-signal pass/fail boundary, deprecated hard-exclude, hash determinism/change-detection) — all passai_summary_generate.py --dry-runagainst real Supabase data onceSUPABASE_URL/SUPABASE_SERVICE_ROLE_KEYare available in the target environment127.0.0.1:7600matchescall_local_queue()'s assumed{"prompt": ...}→{"completion"|"text"|"response"|"result"|"output": ...}contract, or update it🤖 Generated with Claude Code